package com.iplay.web.user;

import java.util.List;

import javax.validation.Valid;
import javax.validation.constraints.NotNull;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.web.PageableDefault;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import com.iplay.configuration.security.jwtAuthentication.JwtFactory;
import com.iplay.configuration.security.jwtAuthentication.auth.UserContext;
import com.iplay.configuration.security.jwtAuthentication.web.TokenExtractor;
import com.iplay.dto.ApiResponse;
import com.iplay.dto.ApiResponseMessage;
import com.iplay.dto.hotel.SimplifiedHotelDTO;
import com.iplay.dto.order.SimplifiedOrderDTO;
import com.iplay.dto.user.UserBasicInfoDTO;
import com.iplay.dto.user.UserCashbackInfoDTO;
import com.iplay.entity.favourites.FavouritesTargetType;
import com.iplay.service.favourites.UserFavouritesService;
import com.iplay.service.order.OrderService;
import com.iplay.service.user.SimplifiedUser;
import com.iplay.service.user.UserService;
import com.iplay.service.user.userInfo.UserInfoService;
import com.iplay.vo.common.EntityIdVO;
import com.iplay.vo.common.SingleValueVO;
import com.iplay.vo.favourites.PostFavouritesVO;
import com.iplay.vo.order.OrderStatusVO;
import com.iplay.vo.user.UserPasswordModificationVO;
import com.iplay.web.configuration.PaginationConfig;
import com.iplay.web.user.UserController.UsernamePredicate;

import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import springfox.documentation.annotations.ApiIgnore;

@RestController
@RequestMapping("/api/user")
public class AuthenticatedUserController {

	@Autowired
	private OrderService orderService;

	@Autowired
	private UserInfoService userInfoService;

	@Autowired
	private UserFavouritesService userFavouritesService;
	
	@Autowired
	private JwtFactory factory;
	
	@Autowired
	private UserService userService;

	@ApiOperation(notes = "用户获得所有与自己相关的订单,包含一个status参数, status有两个可选项：UNFINISHED, FINISHED。例子："
			+ "/api/user/orders?status=UNFINISHED&page=0。默认每页返回20条数据（也可以通过size指定每页的数据条数），"
			+ "按照orderTime降序排列。具体Order对象所有的属性请参考GET::/api/order/{id}", value = "")
	@GetMapping("/orders")
	@PreAuthorize("hasAnyRole('USER', 'MANAGER')")
	public Page<SimplifiedOrderDTO> postOrder(@ApiParam("订单状态") @RequestParam("status") OrderStatusVO vo,
			@ApiParam("分页参数，可选。例子：page=0&size=10&sort=orderTime,desc。每次请求时添加page参数即可，"
					+ "默认情况下size=20, sort=orderTime,desc.") @PageableDefault(value = PaginationConfig.ORDERS_PER_PAGE_FOR_ORDINARY_USER, sort = {
							"orderTime" }, direction = Sort.Direction.DESC) Pageable pageable,
			@ApiIgnore @AuthenticationPrincipal UserContext context) {
		SimplifiedUser authenticatedUser = new SimplifiedUser(context.getUserId(), context.getUsername());
		return orderService.listOrders(authenticatedUser, vo, pageable);
	}

	@ApiOperation(notes = "用户获得自己的返现信息，包括总金额（totalCashback），已完成（completedCashback），处理中（pendingCashback）", value = "")
	@GetMapping("/cashback")
	@PreAuthorize("hasAnyRole('USER', 'MANAGER')")
	public UserCashbackInfoDTO getCashbackInfo(@ApiIgnore @AuthenticationPrincipal UserContext context) {
		return userInfoService.findUserCashbackInfoByUserId(context.getUserId())
				.orElse(new UserCashbackInfoDTO(context.getUserId(), 0, 0, 0));
	}

	@ApiOperation(notes = "用户获得分页展示的收藏的酒店列表", value = "")
	@GetMapping("/favourites")
	@PreAuthorize("hasAnyRole('USER', 'MANAGER')")
	public List<SimplifiedHotelDTO> getFavourites(
			@ApiParam("分页参数，可选。例子：page=0&size=10&sort=createdAt,desc。每次请求时添加page参数即可，"
					+ "默认情况下size=10, sort=createdAt,desc.") @PageableDefault(value = PaginationConfig.FAVOURITES_PER_PAGE, sort = {
							"createdAt" }, direction = Sort.Direction.DESC) Pageable pageable,
			@ApiIgnore @AuthenticationPrincipal UserContext context) {
		return userFavouritesService.findFavouriteHotelsByUserId(context.getUserId(), pageable);
	}

	@ApiOperation(notes = "用户收藏酒店，返回403表示拥护已经收藏过此酒店。", value = "")
	@PostMapping("/favourites/hotels")
	@PreAuthorize("hasAnyRole('USER', 'MANAGER')")
	public boolean saveFavourites(@ApiParam("酒店id实体，包含一个id属性") @RequestBody EntityIdVO vo,
			@ApiIgnore @AuthenticationPrincipal UserContext context) {
		return userFavouritesService.addFavourites(context.getUserId(),
				new PostFavouritesVO(vo.getId(), FavouritesTargetType.HOTEL));
	}

	@ApiOperation(notes = "用户取消收藏酒店，", value = "")
	@DeleteMapping("/favourites/hotels")
	@PreAuthorize("hasAnyRole('USER', 'MANAGER')")
	public boolean deleteFavourites(@ApiParam("酒店id实体，包含一个id属性") @RequestBody EntityIdVO vo,
			@ApiIgnore @AuthenticationPrincipal UserContext context) {
		return userFavouritesService.deleteFavourites(context.getUserId(),
				new PostFavouritesVO(vo.getId(), FavouritesTargetType.HOTEL));
	}

	@ApiOperation(notes = "用户根据酒店id从收藏列表中查询是否存在该酒店,返回null表示不存在", value = "")
	@GetMapping("/favourites/hotels")
	@PreAuthorize("hasAnyRole('USER', 'MANAGER')")
	public SimplifiedHotelDTO findFavouritesByHotelId(@RequestParam("id") int id,
			@ApiIgnore @AuthenticationPrincipal UserContext context) {
		return userFavouritesService.findFavouriteHotelByUserIdAndHotelId(context.getUserId(), id);
	}

	@ApiOperation(notes = "已登陆用户获得自己的基本信息", value = "")
	@GetMapping("/profile")
	@PreAuthorize("hasAnyRole('USER', 'MANAGER')")
	public UserBasicInfoDTO findUserBasicInfo(@ApiIgnore @AuthenticationPrincipal UserContext context) {
		return userInfoService.findUserBasicInfoByUserId(context.getUserId());
	}

	@ApiOperation(notes = "已登陆用户修改自己用户名， 返回ApiResponse对象。isSuccess为false时，data表示用户名修改失败的原因<br>"
	+"<strong>重要！！！</strong>调用此api之后，客户端的token必须丢弃！然后使用新的用户名和密码重新获得token！", value = "")
	//@PutMapping("/profile/username")
	@PreAuthorize("hasAnyRole('USER', 'MANAGER')")
	public ApiResponse<?> updateUsername(@ApiParam("新的用户名") @RequestBody @Valid SingleValueVO vo,
			@ApiIgnore @AuthenticationPrincipal UserContext context) {
		UsernamePredicate p = new UsernamePredicate();
		if (p.test(vo.getValue())) {
			return userInfoService.updateUsername(context.getUserId(), vo.getValue());
		}
		return ApiResponse.createFailApiResponse(ApiResponseMessage.AUTH_USERNAME_INVALID);
	}

	@ApiOperation(notes = "已登陆用户修改手机号", value = "")
	@PutMapping("/profile/phone")
	@PreAuthorize("hasAnyRole('USER', 'MANAGER')")
	public ApiResponse<?> updatePhone(@ApiParam("新的手机号") @RequestBody @Valid SingleValueVO vo,
			@ApiIgnore @AuthenticationPrincipal UserContext context) {
		return userInfoService.updatePhone(context.getUserId(), vo.getValue());
	}

	@ApiOperation(notes = "已登陆用户修改头像", value = "")
	@PostMapping("/profile/avatar")
	@PreAuthorize("hasAnyRole('USER', 'MANAGER')")
	public ApiResponse<?> updateAvatar(@Valid @NotNull MultipartFile file,
			@ApiIgnore @AuthenticationPrincipal UserContext context) {
		return userInfoService.updateAvatar(context.getUserId(), file);
	}

	@ApiOperation(notes = "已登陆用户修改邮箱。调用此api前首先调用/api/auth/applyForRegistrationEmail获得验证邮件，然后调用/api/auth/totp 对totp进行校验，校验成功后获得一个token,再將此token添加Bearer 后放在body中", value = "")
	@PutMapping("/profile/email")
	@PreAuthorize("hasAnyRole('USER', 'MANAGER', 'ADMIN')")
	public ApiResponse<?> updateEmail(
			@ApiParam("包含email信息的token") @RequestBody @Valid SingleValueVO vo,
			@ApiIgnore @AuthenticationPrincipal UserContext context) {
		String token = TokenExtractor.extract(vo.getValue());
		String email = factory.deserializeRegistrationToken(token);
		if (userService.isEmailOccupied(email))
			return ApiResponse.createFailApiResponse(ApiResponseMessage.AUTH_EMAIL_OCCUPIED);
		return userInfoService.updateEmail(context.getUserId(), email);
	}
	
	@ApiOperation(notes = "已登陆用户修改密碼, 密碼修改失敗ApiResponse.success为false，ApiResponse.data即为失败原因 ", value = "")
	@PutMapping("/profile/password")
	@PreAuthorize("hasAnyRole('USER', 'ADMIN', 'MANAGER')")
	public ApiResponse<?> updatePassword(@RequestBody @Valid UserPasswordModificationVO vo,
			@ApiIgnore @AuthenticationPrincipal UserContext context) {
		return userService.updatePassword(context.getUserId(), vo.getPassword(), vo.getNewPassword());
	}
}
